<?php
abstract class EWebby_UI_Widgets_Widget {
	const WIDGETTAG			= 'widget';
	const WIDGETCONTENTTAG	= 'widgetcontainer';
	const FORMIDENTITY 		= '__FORMIDENTITY__';
	const CALLBACKIDENTITY 	= '__CALLBACKIDENTITY__';	
	
	protected static $_callBackElement = null;
	
	protected $_loaded = false;
	protected $_hasDesignView;
	protected $_viewFilePath;
	protected $_document;
	protected $inlineDesign = false;
	public $_node;
	
	public $initEvent;
	public $loadEvent;
	public $preRenderEvent;
	public $renderEvent;
	public $unloadEvent;
	public $elementAddedEvent;
	
	public $isCallBack;
	public $isPostBack;
	public $visible = true;
	public $parent;
	public $page;
	public $elements = array();
	public $attributes;
	public $widgetContainerPath;	
	
	public function __construct() {
		$this->isPostBack = ( (strtolower ( $_SERVER ['REQUEST_METHOD'] ) == 'post')
			&& isset($_POST[self::FORMIDENTITY])
			&& $_POST[self::FORMIDENTITY] == $this->getHashKey() );

		$this->isCallBack = (isset ( $_POST [self::CALLBACKIDENTITY] )
			&& $_POST [self::CALLBACKIDENTITY] == $this->getHashKey() );			
		if( $this->isCallBack ) {
			self::$_callBackElement = $this;
		}
		
		$this->initEvent = new EWebby_Event();
		$this->loadEvent = new EWebby_Event();
		$this->preRenderEvent = new EWebby_Event();
		$this->renderEvent = new EWebby_Event();
		$this->unloadEvent = new EWebby_Event();
		$this->elementAddedEvent = new EWebby_Event();
	}
		

	protected function onInit() {}
	protected function init() {
		$this->attributes = new EWebby_UI_Widgets_WidgetAttributeCollection($this->_node);
		if ($this->parent != null) {
			$this->page = ( $this->parent instanceof EWebby_UI_Page )?
				$this->parent : $this->parent->page;
		}
		
		// init , then init the childs
		$this->onInit();

		// get web design
		$this->_document = new EWebby_UI_Widgets_WidgetDocument($this);
		$documentContent = '';
		if( !($this instanceof EWebby_UI_Widgets_HtmlWidget) ) {
			if( $this->inlineDesign ) {
				$documentContent = EWebby_UI_Widgets_WidgetDocument::getInnerHTML($this->_node);
			} else {
				$this->_viewFilePath = APP_PATH . str_replace('_', DIRECTORY_SEPARATOR, get_class($this)) .
					EWebby_WebApplication::$current->handlersViewExtension;
				if( file_exists($this->_viewFilePath) ) {
					$documentContent = file_get_contents($this->_viewFilePath);
				}
			}
		}

		if( file_exists($this->widgetContainerPath) ) {
			$contentDocument = EWebby_UI_Widgets_WidgetDocument::createDocument(
				file_get_contents($this->widgetContainerPath) );
			if( $contentDocument->replaceWidgetContentWith($documentContent) ) {
				$documentContent = $contentDocument->getContent();
			}
		}
		$this->_document->load($documentContent, !($this instanceof EWebby_UI_Widgets_ITemplateWidget) );

		foreach ( $this->elements as $child ) {
			if( is_array($child) ) {
				foreach ( $child as $innerChild ) {
					$innerChild->init();
				}
			} else {
				$child->init();	
			}
		}
		
		$this->initEvent->dispatchEvent($this, null);		
	}
	
	protected function onLoad() {}
	protected function load() {
		if($this->_loaded) {
			return;
		}
		
		$this->_loaded = true;
		foreach ( $this->elements as $child ) {
			if( is_array($child) ) {
				foreach ( $child as $innerChild ) {
					$innerChild->load();
				}
			} else {
				$child->load();	
			}
		}
		$this->onLoad ();
		
		$this->loadEvent->dispatchEvent($this, null);
	}
	
	protected function onPreRender() {}
	protected function preRender() {
		$this->onPreRender();
		foreach ( $this->elements as $child ) {
			if( is_array($child) ) {
				foreach ( $child as $innerChild ) {
					$innerChild->preRender();
				}
			} else {
				$child->preRender();
			}
		}
		
		$this->preRenderEvent->dispatchEvent($this, null);
		
		// attach the script tag
		if( $this instanceof EWebby_UI_Page ) {
			$headNodes = $this->queryDocument('//head', true);
			if( count($headNodes) == 0 ) {
				$headNodes = $this->queryDocument('//body', true);
			}
			
			if( count($headNodes) > 0 ) {
				$headNodes[0]->appendChild(
					$headNodes[0]->ownerDocument->createCDATASection('<script src="EWebby/ui/core.js" type="text/javascript"></script>')
				);
			}
		}
		
		$formNodes = $this->queryDocument('//form');
		foreach( $formNodes as $formNode ) {
			$supportAjax = true;
			if( $formNode->hasAttribute('ajax') ) {
				$supportAjax = ($formNode->getAttribute('ajax') == 'true');
				$formNode->removeAttribute('ajax');
			}
			
			if( $supportAjax ) {
				$formNode->setAttribute('onsubmit', $formNode->getAttribute('onsubmit') . $this->getClientCallbackScript());
			}
			
			$formNode->appendChild(
				$formNode->ownerDocument->createCDATASection(
					'<input type="hidden" name="' . htmlspecialchars(self::FORMIDENTITY) . 
						'" value="' . htmlspecialchars($this->getHashKey()) . '"/>')
			);
		}
	}
	
	protected function onRender($writer) {}
	protected function render() {
		if ( !$this->visible ) {
			if( $this->_node != null ) {
				$this->_node->parentNode->removeChild($this->_node);
			}
			return;
		}
		
		$writer = new EWebby_UI_Widgets_WidgetWriter();
		$this->onRender($writer);
		foreach ( $this->elements as $child ) {
			if( is_array($child) ) {
				foreach ( $child as $innerChild ) {
					$innerChild->render();
				}
			} else {
				$child->render();
			}
		}
		$this->renderEvent->dispatchEvent($this, $writer);

		$content = $writer . $this->_document->getContent();
		if( $this->_node != null 
			&& !($this instanceof EWebby_UI_Widgets_HtmlWidget) ) {
			if( !$this->inlineDesign ) {
				$content = '<span id="' . htmlspecialchars($this->getHashKey()) . '">' . $content . '</span>';
			}

    		$this->_node->parentNode->replaceChild(
    			$this->_node->ownerDocument->createCDATASection($content),
    			$this->_node);
		}

		return $content;
	}
	
	protected function onUnload() { }
	protected function unload() {
		foreach ( $this->elements as $child ) {
			if( is_array($child) ) {
				foreach ( $child as $innerChild ) {
					$innerChild->unload();
				}
			} else {
				$child->unload();
			}
		}
		$this->onUnload();
		
		$this->unloadEvent->dispatchEvent($this, null);
	}
	
	
	public function containsElement($elementName) {
		return isset( $this->elements[$elementName] );
	}
	public function addElement($name, $element, $content='') {
		$element->parent = $this;

		if( $element->_node == null ) {
			$element->_node = $this->_document->appendNewNode($content);
		}
		
		if( isset($this->elements[$name]) ) {
			if( is_array($this->elements[$name]) ) {
				$this->elements[$name][] = $element;
			} else {
				$firstElement = $this->elements[$name];
				$this->elements[$name] = array($firstElement, $element);
			}
		} else {
			$this->elements[$name] = $element;
		}
		
		$this->elementAddedEvent->dispatchEvent($this, null);
		
		if( $this->_loaded ) {
			$element->init();
			$element->load();
		}
	}
	
	public function queryDocument($xQuery, $deep=false) {
		$result = array();
		$nodes = $this->_document->queryDocument($xQuery);
		if( $nodes != null ) {
			foreach($nodes as $node ) {
				$result[] = $node;
			}
		} 
		
		if( $deep ) {
			foreach ( $this->elements as $child ) {
				if( is_array($child) ) {
					foreach ( $child as $innerChild ) {
						$result = array_merge($result, $innerChild->queryDocument($xQuery, $deep));
					}
				} else {
					$result = array_merge($result, $child->queryDocument($xQuery, $deep));
				}
			}
		}
		
		return $result;
	}
	
	public function getHashKey() {
		return spl_object_hash($this);
	}
	
	public function getClientCallbackScript($parameters='',
		$loadingHandler='',
		$successHandler='', 
		$successHandlerArgs='',
		$failureHandler='',
		$failureHandlerArgs='') {
		$loadingHandler = ($loadingHandler == '')? 'null' : $loadingHandler;
		$successHandler = ($successHandler == '')? 'null' : $successHandler;
		$failureHandler = ($failureHandler == '')? 'null' : $failureHandler;

		return htmlspecialchars('javascript:return EWebby.sendRequest(' .
			'\'' . htmlspecialchars(self::CALLBACKIDENTITY) . '\',' .
			'\'' . htmlspecialchars($this->getHashKey()) . '\',' .
			'\'' . ($parameters) . '\',' .
			htmlspecialchars($loadingHandler) . ',' .
			htmlspecialchars($successHandler) . ',' .
			'\'' . htmlspecialchars($successHandlerArgs) . '\',' .
			htmlspecialchars($failureHandler) . ',' .
			'\'' . htmlspecialchars($failureHandlerArgs) . '\',' .
			'this);');
	}
}